/*
 * Created on 15 oct. 2005
 * @author cschaff
 * Copyright Amadeus 2005
 */
package com.amadeus.spin.standard.util;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;

import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.xml.sax.InputSource;

import com.amadeus.spin.devtool.soap.shooter.Constants;
import com.amadeus.spin.devtool.soap.shooter.FatalException;
import com.amadeus.spin.devtool.soap.shooter.config.ConfigurationParameters;

public final class FileUtils {

  private static Logger log = Logger.getLogger(FileUtils.class);

  private FileUtils() {
  }

  /**
   * Tell if all the files given will be found, e.g. before running stuff with
   * io involved.
   *
   * @param filePathArray
   */
  public static boolean areAllFilesExisting(String[] filePathArray) {
    boolean areAllFilesExisting = true;
    for (int filePathIndex = 0; filePathIndex < filePathArray.length; filePathIndex++) {
      String filePath = filePathArray[filePathIndex];
      if (!fileExists(filePath)) {
        areAllFilesExisting = false;
        break;
      }
    }
    return areAllFilesExisting;
  }

  public static boolean fileExists(String filePath) {
    File file = new File(filePath);
    return file.exists();
  }

  public static List<String> getFilesInDirWithSuffix(String dirPath, String suffix) {
    return getFilesInDirWithSuffix(dirPath, "", suffix);
  }

  /**
   * Returns a List<String> object made of file names with given suffix (i.e.
   * file extension), in a given directory.
   */
  public static List<String> getFilesInDirWithSuffix(String dirPath, String pathPrefix, String suffix) {
    List<String> retList = new ArrayList<String>();
    File baseDirFile = new File(dirPath);
    if (baseDirFile.isDirectory()) {
      String[] childrenFileArray = baseDirFile.list();
      for (int childIndex = 0; childIndex < childrenFileArray.length; childIndex++) {
        String childName = childrenFileArray[childIndex];
        if (childName.endsWith(suffix.toLowerCase())) {
          retList.add(pathPrefix + childName);
        }
      }
    }
    return retList;
  }

  public static FileWriter getWriterOnFile(String filePath) throws IOException {
    File outFile = new File(filePath);
    FileWriter fileWriter = new FileWriter(outFile);
    return fileWriter;
  }

  /**
   * Basic IO utility to write data to a file.
   *
   * @param data
   * @param filename
   * @throws IOException
   *           upon failure (e.g. file not found, or being a directory, etc).
   */
  public static void writeDataToFile(String data, String filename) throws IOException {
    writeDataToFileWithEncoding(data, filename, Dom4jUtils.DEFAULT_ENCODING);
  }

  /**
   * Write a string to a file by specifying the encoding.
   * 
   * @param data
   * @param filename
   * @param encoding
   * @throws IOException
   */
  public static void writeDataToFileWithEncoding(String data, String filename, String encoding) throws IOException {
    File file = new File(filename);
    file.delete();
    FileOutputStream os = null;
    try {
      os = new FileOutputStream(filename);
      byte[] responseInBytesEncodingAware;
      if (encoding == null) {
        responseInBytesEncodingAware = data.getBytes();
      }
      else {
        responseInBytesEncodingAware = data.getBytes(encoding);
      }
      os.write(responseInBytesEncodingAware);
    }
    finally {
      if (os != null) {
        try {
          os.close();
        }
        catch (IOException e) {
          log.error("Unexpected error occured while closing the stream : " + e.getMessage());
        }
      }
    }
  }

  /**
   * Returns a string without trailing file separator
   */
  public static String getNormalizedDirName(String dirName) {
    String normalizedDirName = null;
    if (dirName != null) {
      normalizedDirName = dirName.trim();
      char lastChar = normalizedDirName.charAt(normalizedDirName.length() - 1);
      if (lastChar == '/' || lastChar == '\\') {
        normalizedDirName = normalizedDirName.substring(0, dirName.length() - 1);
      }
    }
    if (normalizedDirName != null) {
      normalizedDirName = normalizedDirName.replaceAll("/\\./", "/");
    }
    return normalizedDirName;
  }

  /**
   * Read a file and return the corresponding String.
   *
   * @throws FatalException
   * @throws IOException
   * @throws UnsupportedEncodingException
   * @throws FileNotFoundException
   */
  public static String readFile(String filePath, String fileEncoding, String scenarioFileName) throws FatalException,
      FileNotFoundException, UnsupportedEncodingException, IOException {
    String ret = null;
    boolean fileWithPathExists = new File(filePath).exists();
    int lastFileIndexForScenario = scenarioFileName.lastIndexOf('/');
    String secondTryScenarioPath;
    if (fileWithPathExists || lastFileIndexForScenario < 0) {
      secondTryScenarioPath = filePath;
    }
    else {
      secondTryScenarioPath = scenarioFileName.substring(0, lastFileIndexForScenario) + File.separator + filePath;
    }
    ret = readFromFileWithEncoding(secondTryScenarioPath, fileEncoding);
    return ret;
  }

  /**
   * reads the content of the file taking into account the encoding.
   *
   * @param filePath
   * @param fileEncoding
   * @return
   * @throws FileNotFoundException
   * @throws IOException
   * @throws UnsupportedEncodingException
   */
  public static String readFromFileWithEncoding(String filePath, String fileEncoding) throws FileNotFoundException,
      IOException, UnsupportedEncodingException {
    String ret = null;
    FileInputStream is = null;
    try {
      is = new FileInputStream(filePath);
      byte[] b = new byte[is.available()];
      is.read(b);
      // Use default platform encoding when none is defined.
      if (fileEncoding != null) {
        ret = new String(b, fileEncoding);
      }
      else {
        ret = new String(b);
      }
    }
    finally {
      if (is != null) {
        try {
          is.close();
        }
        catch (IOException e) {
        }
      }
    }
    return ret;
  }

  /**
   * @param currentFile
   *          Path to the xml file to read
   * @param logger
   * @return
   * @throws FatalException
   */
  public static String getXmlContent(String currentFile, Logger logger) throws FatalException {
    String xmlIn = "";
    String requestFileEncoding;
    try {
      requestFileEncoding = EncodingUtils.getFileEncoding(currentFile);
      xmlIn = readFile(currentFile, requestFileEncoding, ConfigurationParameters.getScenarioFileName());
    }
    catch (IOException e) {
      String errorMsg = "Could not find or read " + currentFile;
      logger.error(errorMsg);
      logger.error("Execution stopped.\n");
      throw new FatalException(errorMsg, e);
    }
    if ((xmlIn == null) || (xmlIn.equals(""))) {
      String errorMsg = currentFile + " is empty.";
      logger.error(errorMsg);
      logger.error("Execution stopped.\n");
      throw new FatalException(errorMsg);
    }
    return xmlIn;
  }

  /**
   * Preprocesses the scenario using the xslt file. Useful to apply if / else statements
   *
   * @param filePath
   * @param paramsFileName
   * @return
   */
  public static ByteArrayOutputStream applyScenarioXsltPreProcessing(String filePath, String paramsFileName) {
    // Apply xslt
    ByteArrayOutputStream baos = null;
    try {
      TransformerFactory tFactory = TransformerFactory.newInstance();
      Transformer transformer = tFactory.newTransformer(new StreamSource(FileUtils.class.getResourceAsStream(
          Constants.SOAPSHOOTER_XSLT)));
      transformer.setParameter("paramsFile", paramsFileName);

      baos = new ByteArrayOutputStream();
      transformer.transform(new StreamSource(filePath), new StreamResult(baos));
    }
    catch (TransformerConfigurationException e) {
      log.warn("Internal error while transforming xml file. " + filePath + " " + e.getMessage());
    }
    catch (TransformerException e) {
      log.warn("Internal error while transforming file. " + filePath + " " + e.getMessage());
    }

    return baos;
  }

  /**
   * Returns the processed xml as a string
   *
   * @param filePath
   * @param paramsFileName
   * @return
   */
  public static String applyScenarioXsltPreProcessingToString(String filePath, String paramsFileName) {
    String result = null;
    String enc = Dom4jUtils.DEFAULT_ENCODING;
    try {
      enc = EncodingUtils.getFileEncoding(filePath);
    }
    catch (IOException e1) {
      log.warn("Could not read encoding of file " + filePath + " " + e1.getMessage());
    }
    try {
      result = FileUtils.applyScenarioXsltPreProcessing(filePath,
          ConfigurationParameters.getParamsFileName()).toString(enc);
    }
    catch (UnsupportedEncodingException e) {
      log.warn("Internal error while transforming xml processed file  " + filePath + " " + e.getMessage());
    }
    return result;
  }

  /**
   * Returns the processed xml as an Inputsource object
   *
   * @param filePath
   * @param paramsFileName
   * @return
   */
  public static InputSource stringToInputSource(String content) {
    ByteArrayInputStream in = new ByteArrayInputStream(content.getBytes());
    InputSource is = new InputSource();
    is.setByteStream(in);
    return is;
  }

  /**
   * @param mixedPath
   * @return
   */
  public static String getFileNameFromPath(String mixedPath) {
    mixedPath = StringUtils.trimToEmpty(mixedPath);
    int lastSlashSeparator = mixedPath.lastIndexOf('/');
    int lastAntiSlashSeparator = mixedPath.lastIndexOf('\\');
    int lastFileSeparator = Math.max(lastAntiSlashSeparator, lastSlashSeparator);
    return mixedPath.substring(lastFileSeparator + 1);
  }
}
